﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Escape
{
    public class Camera
    {

        Matrix view;
        Matrix projection;

        public Vector3 cameraPosition;
        private Vector3 cameraLookAt = Vector3.Zero;

        private GraphicsDevice device;

        private float fov = MathHelper.PiOver4;

        private bool move = false;
        private float moveSpeed = 0.0f;
        private float rotateSpeed = 0.0f;

        float movement;

        int oldX, oldY;

        private Vector3 cameraReference = new Vector3(0, 0, -1);
        float yaw = 0;
        float pitch = 0;

        private Matrix rotation;


        #region properties

        public float FOV
        {
            get { return fov; }
            set
            {
                fov = MathHelper.ToRadians(value / 2.0f);
                CreateMatrices();
            }
        }

        public GraphicsDevice Device
        {
            get { return device; }
            set
            {
                device = value;
                CreateMatrices();
            }
        }

        public Matrix View
        {
            get { return view; }
            set { view = value; }
        }

        public Matrix Projection
        {
            get { return projection; }
            set { projection = value; }
        }

        public Vector3 CameraPosition
        {
            get { return cameraPosition; }
            set
            {
                cameraPosition = value;
                CreateMatrices();
            }
        }

        public Vector3 CameraLookAt
        {
            get { return cameraLookAt; }
            set
            {
                cameraLookAt = value;
                CreateMatrices();
            }
        }

        #endregion

        public Camera(GraphicsDevice d, float pX, float pY, float pZ)
        {
            Device = d;
            CameraPosition = new Vector3(pX, pY, pZ);
        }

        private void CreateMatrices()
        {
            View = Matrix.CreateLookAt(cameraPosition, CameraLookAt, Vector3.Up);
            Projection = Matrix.CreatePerspectiveFieldOfView(fov, Device.Viewport.AspectRatio, 0.05f, 10.0f);
        }


        public void EnableMovement(float moveSpeed, float rotateSpeed)
        {
            move = true;
            this.moveSpeed = moveSpeed;
            this.rotateSpeed = rotateSpeed;
        }

        public void DisableMovement()
        {
            move = false;
        }

        public void setY(float y)
        {
            cameraPosition.Y = y;
        }

        public void Update()
        {

            if (move)
            {

                movement += 0.3f;

                if (Keyboard.GetState().IsKeyDown(Keys.Left))
                    yaw += rotateSpeed;
                if (Keyboard.GetState().IsKeyDown(Keys.Right))
                    yaw -= rotateSpeed;

                yaw -= (Mouse.GetState().X - oldX) * rotateSpeed;

                if (pitch >= -1.5f && pitch <= 1.5f)
                    pitch -= (Mouse.GetState().Y - oldY) * rotateSpeed;

                

                pitch = MathHelper.Clamp(pitch, -1.5f, 1.5f);

                Mouse.SetPosition(Basic.dManager.PreferredBackBufferWidth / 2, Basic.dManager.PreferredBackBufferHeight / 2);
                oldX = Mouse.GetState().X;
                oldY = Mouse.GetState().Y;

                if (Keyboard.GetState().IsKeyDown(Keys.W))
                {
                    Vector3 v = cameraReference * moveSpeed;

                    Matrix forWard = Matrix.CreateRotationY(yaw);
                    v = Vector3.Transform(v, forWard);
                    CameraPosition = GameScreen.currentArea.tryMove(v, CameraPosition);
                    cameraPosition.Y += (float)Math.Sin(movement) * 0.008f;
                }

                if (Keyboard.GetState().IsKeyDown(Keys.S))
                {
                    Vector3 v = -cameraReference * moveSpeed;

                    Matrix forWard = Matrix.CreateRotationY(yaw);
                    v = Vector3.Transform(v, forWard);
                    CameraPosition = GameScreen.currentArea.tryMove(v, CameraPosition);
                    cameraPosition.Y += (float)Math.Sin(movement) * 0.008f;
                }

                if (Keyboard.GetState().IsKeyDown(Keys.D))
                {
                    Vector3 v = Vector3.UnitX * moveSpeed;

                    Matrix forWard = Matrix.CreateRotationY(yaw);
                    v = Vector3.Transform(v, forWard);
                    CameraPosition = GameScreen.currentArea.tryMove(v, CameraPosition);
                    cameraPosition.Y += (float)Math.Sin(movement) * 0.002f;
                }

                if (Keyboard.GetState().IsKeyDown(Keys.A))
                {
                    Vector3 v = -Vector3.UnitX * moveSpeed;

                    Matrix forWard = Matrix.CreateRotationY(yaw);
                    v = Vector3.Transform(v, forWard);
                    CameraPosition = GameScreen.currentArea.tryMove(v, CameraPosition);
                    cameraPosition.Y += (float)Math.Sin(movement) * 0.002f;
                }


                UpdateMatrices();

                Game1.window.Title = "X: " + CameraPosition.X.ToString() + "Y: " + CameraPosition.Y.ToString() + "Z: " + CameraPosition.Z.ToString();

            }
        }

        public BoundingFrustum getFrustum()
        {
            return new BoundingFrustum(View * Projection);
        }


        private void UpdateMatrices()
        {
            rotation = Matrix.CreateFromYawPitchRoll(yaw, pitch, 0);

            Vector3 transformedReference = Vector3.Transform(cameraReference, rotation);
            CameraLookAt = CameraPosition + transformedReference;

        }

        public Ray getPickRay()
        {

            int x = Basic.device.Viewport.Width / 2;
            int y = Basic.device.Viewport.Height / 2;

            Ray ray = new Ray();

            Vector3 nearSource = new Vector3(x, y, 0.0f);
            Vector3 farSource = new Vector3(x, y, 1.0f);

            Matrix world = Matrix.Identity;

            Vector3 nearPoint = Basic.device.Viewport.Unproject(nearSource, projection, view, world);
            Vector3 farPoint = Basic.device.Viewport.Unproject(farSource, projection, view, world);

            Vector3 dir = farPoint - nearPoint;
            dir.Normalize();

            ray.Direction = dir;
            ray.Position = nearPoint;

            return ray;
        }

    }
}